#include <stdio.h>
#include <io.h>
#include <string.h>
#include <alt_types.h>
#include <system.h>
#include <altera_avalon_pio_regs.h>
#include "eth/eth_rx.h"
#include "eth/eth_tx.h"
#include "eth/ethernet.h"
#include "eth/ipv4.h"
#include "eth/arp.h"
#include "eth/udp.h"
#include "eth/ptsi.h"
#include "eth/fifo_to_amm.h"
#include "utility.h"



#define TX_BUF_ETH_OFST		(0x100000)						// Смещение на буфер передатчика сообщений ethernet

#define TX_BUF_ROW_OFST		(0x200000)					// Смещение на буфер передачи строки



#define CURRENT_MAC		0x00AABBCC0001LL
#define BROADCAST_MAC	0xFFFFFFFFFFFFLL
#define CURRENT_IP		conv_ipv4(192, 168, 0 , 253)
#define BROADCAST_IP	conv_ipv4(192, 168, 0 , 255)

#define ROW_DATA_SIZE	640
#define ROW_FRAME_SIZE	IPV4_HEAD_SIZE + UDP_HEAD_SIZE + PTSI_HEAD_SIZE + PTSI_VIDEO_HEAD_SIZE + 640

alt_u8 		frame_num;
alt_u64 	mac_ptsi_req;
alt_u32 	ip_ptsi_req;

// ********************************************




alt_u8 rx_eth_frame()
{
	alt_u8		err 			= 0;
	alt_u16 	size			= 0;
	alt_u32 	rx_frame_addr	= 0;
	alt_u32		payload_ofst	= 0;
	alt_u8*		payload;
	alt_u16 	etype			= 0;
	alt_u32		ip = 0;
	alt_u16 	port			= 0;

	if (eth_rx_get_size() > 0)
	{
		// Считаем адрес пакета.
		rx_frame_addr = eth_rx_get_addr_next() + SDRAM_BASE + 8;

		// Проанализируем пакет. К адресу добавим смещение в 8 байт,
		// чтобы попасть после преамбулы на заголовок
		err = ethernet_frame_parser((alt_u8*)(rx_frame_addr), &size);
		if (err == 0)
		{
			etype = ethernet_get_etype();
			if (etype == ARP)
			{
				eth_tx_set_etype(ARP);
				eth_tx_set_size(size);
				eth_tx_set_dmac(ethernet_get_smac());
//				ipv4_set_destination_ip(ipv4_get_source_ip());
				eth_tx_send(TX_BUF_ETH_OFST, size);
			}
			else if (etype == IP)
			{
				if (ipv4_get_protocol() == UDP)
				{
					ipv4_get_payload(&payload_ofst, &size);
					payload = (alt_u8*)(rx_frame_addr + ETH_HEAD_SIZE + payload_ofst);
					err = ptsi_msg_parser(payload);
					if (err == 0)
					{
						ipv4_set_destination_ip(ipv4_get_source_ip());
						port	= udp_get_rx_sport();
						udp_set_dport(port);
						// Побайтно передаем все сообщение на выход системы, через PIO
						alt_u16	i = size;
						for (i = 0; i < size; i++)
						{
							IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, ((1 << 8) | (*payload++)));
							IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);
						};
					}
				}
			}
		}
		else
		{
			err = 2;
		}
	}
	return err;
}


// ***************************************************
alt_u8 tx_row()
{
	alt_u8		err 		= 0;
	alt_u16 	row_num	= 0;
	alt_u32 	row_addr;

	// Если есть принятые строки и передатчик свободен,
	// проверим состояние счетчика не отправленных сторок, и если он не 0, надо отправлять
	err = fifo_to_amm_get_framenum(&row_num) + eth_tx_get_state();
	if (err == 0)
		 err = ptsi_row_ctrl();

	if (err == 0)
	{
//		usleep(4100);
		// Для проверки времени выполнения операции установим бит в начале передачи
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, (1 << 15));

		// Получим адрес блока с данными строки
		fifo_to_amm_get_addr_next(&row_addr);
		row_addr = row_addr + SDRAM_BASE;

		// Устанавливаем передатчику протокол IP
		eth_tx_set_etype(IP);

		// Добавим IP заголовок
		ipv4_set_protocol(UDP);
		ipv4_create_head(
				(void*)row_addr, ROW_FRAME_SIZE - IPV4_HEAD_SIZE);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, (1 << 14));
		// Добавим UDP заголовок
		udp_create_head(
				(void*)(row_addr + IPV4_HEAD_SIZE),
				ROW_FRAME_SIZE - IPV4_HEAD_SIZE - UDP_HEAD_SIZE);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, (1 << 13));
		// Добавим заголовок видеоданных
		ptsi_create_head_video(
				(void*)(row_addr + IPV4_HEAD_SIZE + UDP_HEAD_SIZE),
				row_num);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, (1 << 12));

//		usleep(3000);
		eth_tx_send(row_addr - SDRAM_BASE, ROW_FRAME_SIZE);
		// Сбросим бит после установки запроса на передачу
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);
	}

	return err;
}

// ************************

int main(void)
{
	alt_u8		err = 0;

	// ТЕСТОВЫЙ БЛОК
//	int i = 0;
//	for (i=0; i < 0x30000; i++)
//	{
//		IOWR_8DIRECT(0x300000+SDRAM_BASE, i, i);
//	}
	// КОНЕЦ ТЕСТОВОГО БЛОКА

	// Настроим приемник ethernet
	eth_rx_init(ETH_RX_0_BASE);
	eth_rx_reset();

	// Настроим передатчик ethernet
	eth_tx_init(ETH_TX_0_BASE);
	eth_tx_reset();
	eth_tx_set_smac(CURRENT_MAC);
	eth_tx_set_dmac(BROADCAST_MAC);

	// Настроим обработчик уровня заголовка ethernet
	ethernet_set_mac(CURRENT_MAC);
	ethernet_set_ip(CURRENT_IP);
	ethernet_set_ask_buf((void*)(TX_BUF_ETH_OFST + SDRAM_BASE));

	// Настроим обработчик уровня заголовка IP
	ipv4_set_source_ip(CURRENT_IP);
	ipv4_set_destination_ip(BROADCAST_IP);

	// Настроим обработчик уровня заголовка UDP
	udp_set_oport(703);
	udp_set_sport(703);
	udp_set_dport(703);

	// Настройка модуля приема строк изображения
	fifo_to_amm_init(FIFO_TO_AMM_0_BASE);
	fifo_to_amm_reset();
	fifo_to_amm_set_fblock_ofst(TX_BUF_ROW_OFST);
	fifo_to_amm_set_frame_ofst(	IPV4_HEAD_SIZE +
								UDP_HEAD_SIZE +
								PTSI_HEAD_SIZE +
								PTSI_VIDEO_HEAD_SIZE);

	// Настройка PtSi
	ptsi_init();

	// Сбросим приемник
	eth_rx_reset();
	while (1)
	{
//		eth_tx_send(TX_BUF_ETH_OFST, 100);
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 1 << 14);
		// Обработка входных пакетов
		err = rx_eth_frame();
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, 0);
		err = tx_row();
	}


	return 0;
};






